`include "defines.v"
`include "inst.v"
module csr(
  input clk,
  input rst_n,
  // 同步写端口
  input                 wen  ,
  input  [11:0]         waddr,
  input  [`XLEN-1:0]    wdata,
  // 读端口
  input  [11:0]         raddr,
  output [`XLEN-1:0]    rdata,
  // wbu in
  input                 wbu_csr_commit_i,
  input  [`EXCEPT:0]    wbu_csr_exception_i,
  input  [`VADDR_W-1:0] wbu_csr_pc_i,
  input  [31:0]         wbu_csr_inst_i,
  // clint interrupt
  output                clintEn,
  input                 clintInterrupt,
  input                 externInterrupt,
  // commit flush instr queue
  output                commit_flush,
  output [`VADDR_W-1:0] commit_addr ,
  output                commit_invalidate,
  output                csr_wbu_isClint_o
);
wire [`XLEN-1:0] mstatus  ; //
reg  [`XLEN-1:0] misa     ; // 
reg  [`XLEN-1:0] medeleg  ;
reg  [`XLEN-1:0] mideleg  ;
wire [`XLEN-1:0] mie      ; // 
reg  [`XLEN-1:0] mtvec    ; //
reg  [`XLEN-1:0] mscratch ; //
reg  [`XLEN-1:0] mepc     ; //
reg  [`XLEN-1:0] mcause   ; //
reg  [`XLEN-1:0] mtval    ;
wire [`XLEN-1:0] mip      ; //
reg  [`XLEN-1:0] mcycle   ; //
reg  [`XLEN-1:0] minstret ; // 
reg  [`XLEN-1:0] marchid  ; // 
reg  [`XLEN-1:0] mimpid   ;
wire [`XLEN-1:0] sstatus  ;//
wire [`XLEN-1:0] sip      ;//
wire [`XLEN-1:0] sie      ;//
reg  [`XLEN-1:0] stvec    ;//
reg  [`XLEN-1:0] sscratch ;//
reg  [`XLEN-1:0] sepc     ;//
reg  [`XLEN-1:0] scause   ;//
reg  [`XLEN-1:0] stval    ;//
reg  [`XLEN-1:0] satp     ;//

// mstatus
reg [1:0] PRV;
wire SD;reg TSR;reg TW;reg TVM;reg MXR;reg SUM;reg MPRV;
reg [1:0] XS;reg [1:0] FS;
reg [1:0] MPP;reg SPP;
reg MPIE;reg SPIE;reg UPIE;
reg MIE; reg SIE ;reg UIE;
//mip
reg MEIP;reg SEIP;reg UEIP;
reg MTIP;reg STIP;reg UTIP;
reg MSIP;reg SSIP;reg USIP;
//mie
reg MEIE;reg SEIE;reg UEIE;
reg MTIE;reg STIE;reg UTIE;
reg MSIE;reg SSIE;reg USIE;
wire isMret        = wbu_csr_commit_i && wbu_csr_exception_i[`mret]         ;
wire isSret        = wbu_csr_commit_i && wbu_csr_exception_i[`sret]         ;
wire isUret        = wbu_csr_commit_i && wbu_csr_exception_i[`uret]         ;
wire isEcall       = wbu_csr_commit_i && wbu_csr_exception_i[`ecall]        ;
wire isClint       = wbu_csr_commit_i && MTIE && MTIP && MIE ;
wire isExInt       = MEIE && MEIP && MIE ;
wire isilegalInstr = wbu_csr_commit_i && wbu_csr_exception_i[`ilginst]      ;
wire isFenceI      = wbu_csr_commit_i && wbu_csr_exception_i[`fencei]       ;
wire isWcsr        = wbu_csr_commit_i && wen;
wire Mmode         = (PRV == `PRV_M);
wire Smode         = (PRV == `PRV_S);
wire Umode         = (PRV == `PRV_U);
// wire exceptionIntoM = ((Mmode | (Smode & ~medeleg[])) && isClint)|
//                       ((Mmode | (Smode & ~medeleg[])) && isExInt);
// wire exceptionIntoS = ((Smode | (Mmode & medeleg[])) && isClint)|
//                       ((Smode | (Mmode & medeleg[])) && isExInt);

assign commit_invalidate = isFenceI;
assign commit_flush  = isEcall || isMret || isClint || isFenceI || isExInt || isWcsr;
assign commit_addr   = (isFenceI || isWcsr) ? wbu_csr_pc_i + 'd4           :
                       isExInt  ? {mtvec[`VADDR_W-1:2],2'd0}   :
                       isClint  ? {mtvec[`VADDR_W-1:2],2'd0}   :
                       isEcall  ? {mtvec[`VADDR_W-1:2],2'd0}   :
                       isMret   ? {mepc[`VADDR_W-1:2] ,2'd0}   :
                       `ZERO;
assign clintEn       = MTIE;
assign csr_wbu_isClint_o = isClint;
// FIND:example
// always@(posedge clk or negedge rst_n)
//   if(~rst_n)
//     t <= `ZERO;
//   else if(wen && (waddr == `CSR_ADDR_xxx))
//     t <= wdata;
// FIND:mtval
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    mtval <= `ZERO;
  else if(wen & (waddr == `CSR_ADDR_mtval))
    mtval <= wdata;
// FIND:stval
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    stval <= `ZERO;
  else if(wen & (waddr == `CSR_ADDR_stval))
    stval <= wdata;
// FIND:satp
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    satp <= `ZERO;
  else if(wen & (waddr == `CSR_ADDR_satp))
    satp <= wdata;
// FIND:mcycle
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    mcycle <= `ZERO;
  else 
    mcycle <= mcycle + 1'b1;
// FIND:minstret
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    minstret <= `ZERO;
  else if(wbu_csr_commit_i) 
    minstret <= minstret + 1'b1;
// FIND:marchid
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    marchid <= `XLEN'h48454c4c4f534a48;
// FIND:misa
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    misa <= `XLEN'h1000000000000100;
// FIND:mstatus
always@(posedge clk or negedge rst_n)
  if(~rst_n)begin
    TSR  <= `N;  
    TW   <= `N;    
    TVM  <= `N;  
    MXR  <= `N;    
    SUM  <= `N;  
    MPRV <= `N;
    FS   <= `ZERO;  
    XS   <= `ZERO;  
    PRV  <= `PRV_M;    
    MPP  <= `ZERO;  
    SPP  <= `ZERO;
    MPIE <= `N;
    SPIE <= `N;
    UPIE <= `N;
    MIE  <= `N;
    SIE  <= `N;
    UIE  <= `N;
  end else if(wen && (waddr == `CSR_ADDR_mstatus)) begin
    TSR  <= wdata[22];
    TW   <= wdata[21];
    TVM  <= wdata[20];
    MXR  <= wdata[19];
    SUM  <= wdata[18];
    MPRV <= wdata[17];
    XS   <= wdata[16:15];
    FS   <= wdata[14:13];
    MPP  <= wdata[12:11];
    SPP  <= wdata[8];
    MPIE <= wdata[7];
    SPIE <= wdata[5];
    UPIE <= wdata[4];
    MIE  <= wdata[3];
    SIE  <= wdata[1];
    UIE  <= wdata[0];
  end else if(wen && (waddr == `CSR_ADDR_sstatus)) begin
    MXR  <= wdata[19];
    SUM  <= wdata[18];
    XS   <= wdata[16:15];
    FS   <= wdata[14:13];
    SPP  <= wdata[8];
    SPIE <= wdata[5];
    UPIE <= wdata[4];
    SIE  <= wdata[1];
    UIE  <= wdata[0];
  end else if (isEcall || isClint || isExInt) begin
    MPIE <= MIE;
    MIE  <= `N;
    MPP  <= PRV;
    PRV  <= `PRV_M;
  end else if(isMret)begin
    MIE  <= MPIE;
    MPIE <= `Y;
    PRV  <= MPP;
    MPP  <= `PRV_M;
  end
assign SD      = ((FS == 2'b11)||(XS == 2'b11));
assign mstatus = {SD,37'd0,2'd2,2'd2,9'd0,FS,MPP,3'd0,MPIE,3'd0,MIE,3'd0};
assign sstatus = {SD,35'd0,2'd2,12'd0,MXR,SUM,1'b0,XS,FS,4'd0,SPP,2'd0,SPIE,UPIE,2'd0,SIE,UIE};
// FIND:mip
always@(posedge clk or negedge rst_n)
  if(~rst_n)begin
    MTIP <= `N;
    STIP <= `N;
    UTIP <= `N;
  end else if(wen && (waddr == `CSR_ADDR_mip))begin
    MTIP <= wdata[7];
    STIP <= wdata[5];
    UTIP <= wdata[4];
  end else if(wen && (waddr == `CSR_ADDR_sip))begin
    STIP <= wdata[5];
    UTIP <= wdata[4];
  end else begin
    MTIP <= clintInterrupt;
    STIP <= clintInterrupt;
    UTIP <= clintInterrupt;
  end
always@(posedge clk or negedge rst_n)
  if(~rst_n)begin
    MEIP <= `N;
    SEIP <= `N;
    UEIP <= `N;
  end else if(wen && (waddr == `CSR_ADDR_mip))begin
    MEIP <= wdata[11];
    SEIP <= wdata[9];
    UEIP <= wdata[8];
  end else if(wen && (waddr == `CSR_ADDR_sip))begin
    SEIP <= wdata[9];
    UEIP <= wdata[8];
  end else begin
    MEIP <= externInterrupt;
    SEIP <= externInterrupt;
    UEIP <= externInterrupt;
  end
always@(posedge clk or negedge rst_n)
  if(~rst_n)begin
    MSIP <= `N;
    SSIP <= `N;
    USIP <= `N;
  end else if(wen && (waddr == `CSR_ADDR_mip))begin
    MSIP <= wdata[3];
    SSIP <= wdata[1];
    USIP <= wdata[0];
  end else if(wen && (waddr == `CSR_ADDR_sip))begin
    SSIP <= wdata[1];
    USIP <= wdata[0];
  end
assign mip = {52'd0,MEIP,1'b0,SEIP,UEIP,MTIP,1'b0,STIP,UTIP,MSIP,1'b0,SSIP,USIP};
assign sip = {52'd0,1'b0,1'b0,SEIP,UEIP,1'b0,1'b0,STIP,UTIP,1'b0,1'b0,SSIP,USIP};
// FIND:mie
always@(posedge clk or negedge rst_n)
  if(~rst_n)begin
    MTIE <= `N;
    STIE <= `N;
    UTIE <= `N;
  end else if(wen && (waddr == `CSR_ADDR_mie))begin
    MTIE <= wdata[7];
    STIE <= wdata[5];
    UTIE <= wdata[4];
  end else if(wen && (waddr == `CSR_ADDR_sie))begin
    STIE <= wdata[5];
    UTIE <= wdata[4];
  end 
always@(posedge clk or negedge rst_n)
  if(~rst_n)begin
    MEIE <= `N;
    SEIE <= `N;
    UEIE <= `N;
  end else if(wen && (waddr == `CSR_ADDR_mie))begin
    MEIE <= wdata[11];
    SEIE <= wdata[9];
    UEIE <= wdata[8];
  end else if(wen && (waddr == `CSR_ADDR_sie))begin
    SEIE <= wdata[9];
    UEIE <= wdata[8];
  end 
always@(posedge clk or negedge rst_n)
  if(~rst_n)begin
    MSIE <= `N;
    SSIE <= `N;
    USIE <= `N;
  end else if(wen && (waddr == `CSR_ADDR_mie))begin
    MSIE <= wdata[3];
    SSIE <= wdata[1];
    USIE <= wdata[0];
  end else if(wen && (waddr == `CSR_ADDR_sie))begin
    SSIE <= wdata[1];
    USIE <= wdata[0];
  end
assign mie = {52'd0,MEIE,1'b0,SEIE,UEIE,MTIE,1'b0,STIE,UTIE,MSIE,1'b0,SSIE,USIE};
assign sie = {52'd0,1'b0,1'b0,SEIE,UEIE,1'b0,1'b0,STIE,UTIE,1'b0,1'b0,SSIE,USIE};
// FIND:mcause
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    mcause <= `ZERO;
  else if(wen && (waddr == `CSR_ADDR_mcause))begin
    mcause <= wdata;
  end else if(isClint) begin
    mcause <= {1'b1,`Cause_clint};
  end else if (isEcall) begin
    mcause <= {1'b0,`Cause_machine_ecall};
  end else if (isExInt)begin
    mcause <= {1'b1,`Cause_extInterrupt};
  end
// FIND:scause
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    scause <= `ZERO;
  else if(wen && (waddr == `CSR_ADDR_scause))begin
    scause <= wdata;
  end 
// FIND:mtvec
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    mtvec <= `ZERO;
  else if(wen && (waddr == `CSR_ADDR_mtvec))begin
    mtvec <= wdata;
  end
// FIND:stvec
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    stvec <= `ZERO;
  else if(wen && (waddr == `CSR_ADDR_stvec))begin
    stvec <= wdata;
  end
// FIND:mepc
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    mepc <= `ZERO;
  else if(wen && (waddr == `CSR_ADDR_mepc))begin
    mepc <= wdata;//[`VADDR_W-1:2];
  end else if(isEcall || isClint || isExInt) begin
    mepc[`VADDR_W-1:2] <= wbu_csr_pc_i[`VADDR_W-1:2];
  end
// FIND:sepc
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    sepc <= `ZERO;
  else if(wen && (waddr == `CSR_ADDR_sepc))begin
    sepc <= wdata;//[`VADDR_W-1:2];
  end 
// FIND:mscratch
always@(posedge clk or negedge rst_n)
  if(~rst_n)  
    mscratch <= `ZERO;
  else if(wen && (waddr == `CSR_ADDR_mscratch))begin
    mscratch <= wdata;    
  end
// FIND:sscratch
always@(posedge clk or negedge rst_n)
  if(~rst_n)  
    sscratch <= `ZERO;
  else if(wen && (waddr == `CSR_ADDR_sscratch))begin
    sscratch <= wdata;    
  end
// read logical 
reg  [`XLEN-1:0] out;
always@(*)begin
  case(raddr)
    `CSR_ADDR_mstatus  : out = mstatus  ;
    `CSR_ADDR_misa     : out = misa     ;
    `CSR_ADDR_medeleg  : out = medeleg  ;
    `CSR_ADDR_mideleg  : out = mideleg  ;
    `CSR_ADDR_mie      : out = mie      ;
    `CSR_ADDR_mtvec    : out = mtvec    ;
    `CSR_ADDR_mscratch : out = mscratch ;
    `CSR_ADDR_mepc     : out = mepc     ;
    `CSR_ADDR_mcause   : out = mcause   ;
    `CSR_ADDR_mtval    : out = mtval    ;
    `CSR_ADDR_mip      : out = mip      ;
    `CSR_ADDR_mcycle   : out = mcycle   ;
    `CSR_ADDR_minstret : out = minstret ;
    `CSR_ADDR_marchid  : out = marchid  ;
    `CSR_ADDR_mimpid   : out = mimpid   ;
    `CSR_ADDR_sstatus  : out = sstatus  ;
    `CSR_ADDR_sip      : out = sip      ;
    `CSR_ADDR_sie      : out = sie      ;
    `CSR_ADDR_stvec    : out = stvec    ;
    `CSR_ADDR_sscratch : out = sscratch ;
    `CSR_ADDR_sepc     : out = sepc     ;
    `CSR_ADDR_scause   : out = scause   ;
    `CSR_ADDR_stval    : out = stval    ;
    `CSR_ADDR_satp     : out = satp     ;
    default:out = `ZERO;
  endcase
end
assign rdata = out;

`ifdef DIFFTEST
reg [31:0]intrNOR ;
reg [`VADDR_W-1:0] exPC;
reg [31:0] exInst;

always@(posedge clk or negedge rst_n)
  if(~rst_n)begin
    intrNOR <= `ZERO;
    exPC    <= `ZERO;
    exInst  <= `ZERO;
  end else begin
    intrNOR <= isClint ? mcause[31:0] : `ZERO;
    exPC    <= wbu_csr_pc_i  ;
    exInst  <= wbu_csr_inst_i;
  end
reg t;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    t <= `N;
  else 
    t <= isClint;
DifftestArchEvent DifftestArchEvent(
  .clock (clk),
  .coreid('d0),
  .intrNO (t ? mcause[31:0] : `ZERO),
  .cause(`ZERO),
  .exceptionPC   ( exPC    ),
  .exceptionInst ( exInst  )
);

DifftestCSRState DifftestCSRState(
  .clock              (clk),
  .coreid             (0),
  .priviledgeMode     (PRV),
  .mstatus            (mstatus),
  .sstatus            (sscratch),
  .mepc               (mepc),
  .sepc               (sepc),
  .mtval              (mtval),
  .stval              (stval),
  .mtvec              (mtvec),
  .stvec              (stvec),
  .mcause             (mcause),
  .scause             (scause),
  .satp               (satp  ),
  .mip                (mip   ),
  .mie                (mie),
  .mscratch           (mscratch),
  .sscratch           (sscratch),
  .mideleg            (mideleg ),
  .medeleg            (medeleg )
);
`endif
endmodule